#include "CentralCache.h"
#include "PageCache.h"

CentralCache CentralCache::_sInstance;//定义

Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
    //查看当前的spanlist中是否有未分配对象的span
    Span* it = list.Begin();
    while(it != list.End())
    {
        //判断span是否有内存块
        if(it->_freeList != nullptr)
        {
            return it;
        }
        else//没有就继续往后面的span中找
        {
            it = it->_next;
        }
    }
    //先将central cache的桶解锁，这样其他线程释放内存对象时不会产生阻塞
    list._mutex.unlock();
    //没有空闲的span，向page申请
    PageCache::GetInstance()->_pageMutex.lock();
    Span *span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
    PageCache::GetInstance()->_pageMutex.unlock();
    //保存内存块大小
    span->_objSize = size;
    //计算span大块内存的起始地址和大小
    char* start = (char*)(span->_pageId << PAGE_SHIFT);
    size_t bytes = span->_num << PAGE_SHIFT;
    char* end = start + bytes;

    //把大块内存切成小块用自由链表连接起来
    span->_freeList = start;
    start += size;
    void* tail = span->_freeList;//链表头不动，用tail去随start切分
    while(start < end)
    {
        //tail随start不断向后移，移一个对应size大小就插入到自由链表中
        NextObj(tail) = start;
        tail = NextObj(tail);//tail = start
        start += size;
    }
    list._mutex.lock();//要对centralCache中的桶里的spanlist添加一个span，加锁
    list.PushFront(span);
    return span;
}


size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
    size_t index = SizeClass::Index(size);//映射central cache桶号
    _spanLists[index]._mutex.lock();//不同线程可能同时向同一个桶里申请内存，所以加桶锁
    Span* span = GetOneSpan(_spanLists[index], size);//从桶里找span，有就直接用，没有就向page cache申请

    //从span 中获取batchNum个对象，如果不够有多少拿多少
    start = span->_freeList;
    end = start;
    //到这里其实已经获取了一个对象了
    size_t count = 0;
    size_t actualNum = 1;
    while ((count < batchNum - 1) && (NextObj(end) != nullptr))
    {
        end = NextObj(end);//end指针不断随自由链表往后偏移
        ++count;
        ++actualNum;
    }
    //从span中切分出来
    span->_freeList = NextObj(end);
    NextObj(end) = nullptr;

    _spanLists[index]._mutex.unlock();
    
    return actualNum;
    
}

void CentralCache::ReleaseListToSpan(void* start, size_t size)
{
    size_t index = SizeClass::Index(size);//找到对应桶
    _spanLists[index]._mutex.lock();//桶上锁
    while(start)//将内存块逐个放到对应span中
    {
        void* next = NextObj(start);
        Span* span = PageCache::GetInstance()->MapObjectToSpan(start);
        //将对象头插入span的自由链表中
        NextObj(start) = span->_freeList;
        span->_freeList = start;
        span->_useCount --;//更新分配给threadcache的计数
        if(span->_useCount == 0)//这个span分配出去的对象全部返回
        {
            //这个span可以回收给page cache，page cache 尝试做前后页合并
            _spanLists[index].Erase(span);
            span->_freeList = nullptr;
            span->_next = nullptr;
            span->_pre = nullptr;
            //释放span 给page cache，使用page cache的整体锁，解开central cache 的桶锁
            _spanLists[index]._mutex.unlock();//解开桶锁
            PageCache::GetInstance()->_pageMutex.lock();//page cache 上锁
            PageCache::GetInstance()->ReleaseSpanToPageCache(span);
            PageCache::GetInstance()->_pageMutex.unlock();//page解锁
            _spanLists[index]._mutex.lock();//central上锁
        }
        start = next;
    }
    _spanLists[index]._mutex.unlock();//解桶锁
}